security: keep OpenWorker's own sidecar token out of the shell environment - #295
Open
Mr-Neutr0n wants to merge 1 commit into
Open
Conversation
…nment
LocalExecutor built the shell env as {**os.environ, ...}, so every command
run_shell executes inherited the whole server process environment - including
COWORKER_API_TOKEN.
Measured, with the token and a provider key set on the server process:
run_shell(`echo "token=$COWORKER_API_TOKEN key=$OPENAI_API_KEY"`)
-> token=SIDECAR-SECRET-abc123 key=sk-user-key-xyz
That token authenticates every request to the local API
(server/app.py::require_sidecar_token). It is the only thing standing between a
process on this machine and the agent's shell and file tools, and the whole
point of run_shell is executing project code - build scripts, test suites, npm
lifecycle hooks. Any of those could read it back out and then drive the API
directly.
The repo already draws this line elsewhere: verify.scrubbed_env() exists so
"target-repo code never sees our credentials" (DOMAIN_MODEL §2) and is applied
to agent CLI sandboxes and local test runs. run_shell was the gap.
Scoped narrowly on purpose. Only OpenWorker's own credential is withheld; the
user's provider keys stay in the environment so builds, gh, and everything else
keep working. Widening this to provider keys is a real question but a UX
decision, not a bug fix, so it is left alone here.
Tests: tests/test_shell_env_isolation.py - the token is absent from both $VAR
expansion and a full `env` listing, the user's own keys survive, PATH survives,
and the explicit env= constructor argument is unchanged.
Mr-Neutr0n
force-pushed
the
security/shell-does-not-inherit-sidecar-token
branch
from
July 28, 2026 20:52
7eddcf6 to
1d7860f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LocalExecutorbuilds the shell environment as{**os.environ, ...}, so every commandrun_shellexecutes inherits the whole server process environment — includingCOWORKER_API_TOKEN.Measured
With the token and a provider key set on the server process:
After this change:
Why the token specifically
COWORKER_API_TOKENauthenticates every request to the local API (server/app.py::require_sidecar_token). It's the only thing standing between a process on this machine and the agent's shell and file tools — and it's what stops a fetch ofhttp://127.0.0.1:<port>/from driving the sidecar (relevant to #292).The whole point of
run_shellis executing project code: build scripts, test suites, npm lifecycle hooks. Any of those can read the token out of its environment and then drive the local API directly.echo $COWORKER_API_TOKENis the demo;env | curl -d @-is the attack.The repo already draws this line elsewhere.
verify.scrubbed_env()exists so "target-repo code never sees our credentials" (DOMAIN_MODEL §2) and is applied to agent CLI sandboxes and local test runs.run_shellwas the gap.Scope
Deliberately narrow: only OpenWorker's own credential is withheld. The user's provider keys stay in the environment so builds,
gh, and everything else keep working.Whether provider keys should also be scrubbed from
run_shellis a real question — the agent has connector tools for GitHub and friends, so the shell arguably doesn't need those either — but that's a UX decision with real breakage risk, not a bug fix. Happy to follow up if you want it.Tests
tests/test_shell_env_isolation.py— the token is absent from both$VARexpansion and a fullenvlisting (echoisn't the only way to read it), the user's own keys survive,PATHsurvives, and the explicitenv=constructor argument is unchanged.Note these tests import the new helper, so they can't run against the unpatched tree — the before/after above is the direct evidence instead.
Full suite: 921 passed. The 14 failures reproduce on a clean checkout —
test_fake_slack.py(dead-port timeouts, #252) andtest_bedrock_provider.py(missingbedrockextra, #284/#285).Reviewed and tested locally; drafted with AI assistance.